package com.zj.auth.config;

import cn.zj.smart.util.StringUtil;
import cn.zj.smart.util.exception.AuthException;
import cn.zj.smart.util.json.JsonUtil;
import com.zj.auth.anno.IgnoreAuth;
import com.zj.auth.bean.AuthCode;
import com.zj.auth.bean.TokenTo;
import com.zj.auth.cache.RedisUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

@Component
public class AuthorizationInterceptor implements HandlerInterceptor {
    private static final Logger log = LoggerFactory.getLogger(AuthorizationInterceptor.class);
    @Autowired
    private AuthConfig authConfig;
    @Autowired
    private RedisUtil redisUtil;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception{
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        if (method.getAnnotation(IgnoreAuth.class) != null){
            return true;
        }

        String tokenJson = request.getHeader(authConfig.getTokenHeaderName());
        String uri = request.getRequestURI();
        boolean checkResult = checkAuthToken(uri, tokenJson);
        if (checkResult) {
            TokenTo tokenTo = JsonUtil.parse(tokenJson, TokenTo.class);
            // TODO: 2020/11/20 看这里需要在注入RequestAttribute不
//            request.setAttribute(Constant.CUR_MEMBER_KEY, tokenTo.getMemberId());
            return true;
        }
        log.warn("[{}] auth validate error", uri);
        throw new AuthException(AuthCode.NEED_LOGIN);
    }

    public boolean checkAuthToken(String url, String tokenJson) {
        log.debug(tokenJson);
        if (StringUtil.isBlank(tokenJson)) {
            log.warn("tokenJson is null");
            throw new AuthException(AuthCode.TOKEN_ERROR);
        }
        TokenTo tokenTo = JsonUtil.parse(tokenJson, TokenTo.class);
        if (!authConfig.getApiVersion().equals(tokenTo.getApiVersion())) {
            log.warn("api version error : " + tokenTo.getApiVersion());
            throw new AuthException(AuthCode.TOKEN_ERROR);
        }
        // 2019/11/25 时差不能超过5分钟
        final long sendTime = tokenTo.getSendTime();
        if (System.currentTimeMillis() - sendTime > 1000 * 60 * 5) {
            log.warn("send time error:" + sendTime);
            throw new AuthException(AuthCode.TOKEN_ERROR);
        }

        final String token = tokenTo.getToken();
        String memberId = redisUtil.getCustomerId(token);
        if (null == memberId) {
            throw new AuthException(AuthCode.TOKEN_ERROR);
        }
        if (!memberId.equals(tokenTo.getMemberId() + "")) {
            throw new AuthException(AuthCode.TOKEN_ERROR);
        }
//      md5(url+"盐值"+memberId+token(去掉前三位和后三位)+appVersion+udid+apiVersion+platformType+sendTime)
        String signToken = token.substring(3, token.length() - 3);
        log.debug("[{}] ======token====== [{}]", token, signToken);
        StringBuffer sb = new StringBuffer(url)
                .append(authConfig.getAuthSalt())
                .append(memberId)
                .append(signToken)
                .append(tokenTo.getAppVersion())
                .append(tokenTo.getUdid())
                .append(tokenTo.getApiVersion())
                .append(tokenTo.getPlatformType())
                .append(tokenTo.getSendTime());
        String signature = DigestUtils.md5DigestAsHex(sb.toString().getBytes());
        if (!signature.equals(tokenTo.getSignature())) {
            log.warn("auth validate error : sign[{}] != signature[{}]", signature, tokenTo.getSignature());
            throw new AuthException(AuthCode.SIGNATURE_ERROR);
        }
        return true;
    }
}